Public
Edited
Oct 18, 2023
Paused
8 forks
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require('d3@7')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// extract the 10 most populous countries in 2005
listData = gapminder
.filter(d => d.year === 2005)
.sort((a, b) => b.pop - a.pop)
.slice(0, 10)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// Run this cell to recolor just the first item in the list above.
// If you are curious rhe randomColor() function was imported as a utility at the end of this document.
d3.select('li.country').style('color', randomColor()), null
Insert cell
// Run this cell to recolor ALL items in the list above
d3.selectAll('li.country').style('color', randomColor()), null
Insert cell
Insert cell
{
const ol = d3.create('ol');
ol.selectAll('li') // select all list elements (orange circle below)
.data(listData) // bind all our data values (blue circle below)
return ol.node();
}
Insert cell
Insert cell
selection = {
const ol = d3.create('ol');
return ol.selectAll('li').data(listData);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const ol = d3.create('ol');

ol.selectAll('li') // select all list elements (orange circle above)
.data(listData) // bind all our data values (blue circle above)
.join('li') // merge the 'enter' and 'update' sets, create 'li' elements as needed
.text(d => `${d.country}: ${d.pop}`) // For data-bound selections, transformation operators can be
// functions that receive a data value as an input argument.
return ol.node();
}
Insert cell
Insert cell
{
const ol = d3.create('ol');
ol.selectAll('li') // select all list elements (orange circle above)
.data(listData) // bind all our data values (blue circle above)
.join(
enter => enter.append('li'), // append an li element for each entering item
update => update, // do nothing with items that match an existing element
exit => exit.remove() // remove li elements whose backing data is now gone
)
.text(d => `${d.country}: ${d.pop}`)
return ol.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const ol = d3.select('ol#enter-update-exit');

// use a new dataset, manipulable with the variables defined below
const newData = gapminder
.filter(d => d.year === year)
.sort((a, b) => b.pop - a.pop)
.slice(0, n);
ol.selectAll('li') // select all list elements (orange circle above)
.data(newData) // bind all our data values (blue circle above)
.join(
enter => enter.append('li'), // hint: add code here
update => update, // hint: add code here
exit => exit.remove()
)
.text(d => `${d.country}: ${d.pop}`);
}
Insert cell
Insert cell
year = 2005
Insert cell
n = 10
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const container = d3.create('div');
// Add code here to generate the bar chart within the container.
// Use 'div' elements for each bar.
// Make sure you set some text to each div in order to make sure the bars appear.
// The 'barData' variable holds the data to visualize as a set
// of objects with 'key' and 'value' properties.
return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
md`The current page \`width\` variable = ${width} px.` // Observable's built-in width property!
Insert cell
xscale = d3.scaleLinear()
.domain([0, d3.max(barData, d => d.value)])
.range([0, width])
Insert cell
Insert cell
xscale(49)
Insert cell
Insert cell
{
const container = d3.create('div');

container.selectAll('div')
.data(barData)
.join('div')
.style('background', 'steelblue')
.style('border', '1px solid white')
.style('font-size', 'small')
.style('color', 'white')
.style('text-align', 'right')
.style('padding', '3px')
.style('width', d => `${xscale(d.value)}px`) // <-- Use xscale instead of a magic number
.text(d => d.value);
return container.node();
}
Insert cell
Insert cell
color = d3.scaleOrdinal(d3.schemeTableau10).domain(barData.map(d => d.key));
// Default color schemes are here: https://github.com/d3/d3-scale-chromatic#categorical
// Try another one!
Insert cell
{
const container = d3.create('div');

container.selectAll('div')
.data(barData)
.join('div')
.style('background', d => color(d.key))
.style('border', '1px solid white')
.style('font-size', 'small')
.style('color', 'white')
.style('text-align', 'right')
.style('padding', '3px')
.style('width', d => `${xscale(d.value)}px`)
.text(d => d.value);

return container.node();
}
Insert cell
Insert cell
Insert cell
{
const barHeight = 25;
const height = barData.length * barHeight;

// create svg element, specify total width and height
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

// add SVG 'rect' elements for bars
container.selectAll('rect')
.data(barData)
.join('rect')
.attr('x', 0)
.attr('y', (d, i) => i * barHeight)
.attr('width', d => xscale(d.value))
.attr('height', barHeight)
.style('fill', d => color(d.key))
.style('stroke', 'white');

// add SVG 'text' elements for labels
container.selectAll('text')
.data(barData)
.join('text')
.attr('x', d => xscale(d.value))
.attr('y', (d, i) => i * barHeight)
.attr('dx', -20)
.attr('dy', '1.25em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

// return SVG DOM element
return container.node();
}
Insert cell
Insert cell
Insert cell
height = 150
Insert cell
yscale = d3.scaleBand()
.domain(barData.map(d => d.key))
.range([height, 0]) // Try flipping this to [0, height] and see the effect on the bars below.
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

container.selectAll('rect')
.data(barData)
.join('rect')
.attr('x', 0)
.attr('y', d => yscale(d.key)) // <-- Use yscale instead of manually positioning bars
.attr('width', d => xscale(d.value))
.attr('height', yscale.bandwidth()) // <-- Band scales split a pixel range into equal-sized bands
.style('fill', d => color(d.key))
.style('stroke', 'white');

container.selectAll('text')
.data(barData)
.join('text')
.attr('x', d => xscale(d.value))
.attr('y', d => yscale(d.key)) // <-- Use yscale instead of manually positioning labels
.attr('dx', -20)
.attr('dy', '1.2em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

return container.node();
}
Insert cell
Insert cell
Insert cell
{
const width = 400;
const height = 200;

const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

// Redefine the x scale here.
const xscale = /* add code here */;

// Redefine the y scale here.
const yscale = /* add code here */;

container.selectAll('rect')
.data(barData)
.join('rect')
.attr('x', d => /* add code here */)
.attr('y', d => /* add code here */)
.attr('width', d => /* add code here */)
.attr('height', d => /* add code here */)
.style('fill', d => color(d.key))
.style('stroke', 'white');

container.selectAll('text')
.data(barData)
.join('text')
.attr('x', d => /* add code here */)
.attr('y', d => /* add code here */)
.attr('dx', /* add code here */)
.attr('dy', '1em')
.attr('fill', 'white')
.style('font-size', 'small')
.style('text-anchor', 'middle')
.text(d => d.value);
return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

const g = container.selectAll('g')
.data(barData)
.enter() // <-- We use "enter" (not "join") so we can later append <rect> and <text> only for new data.
.append('g')
.attr('transform', d => `translate(0, ${yscale(d.key)})`);

g.append('rect')
// We no longer need to bind data to <rect>, or specify y positions.
.attr('width', d => xscale(d.value))
.attr('height', yscale.bandwidth())
.style('fill', d => color(d.key))
.style('stroke', 'white');

g.append('text')
// We no longer need to bind data to <text>, or specify y positions.
.attr('x', d => xscale(d.value))
.attr('dx', -20)
.attr('dy', '1.2em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);
return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
margin = ({top: 10, right: 10, bottom: 20, left: 20})
Insert cell
Insert cell
xMargin = xscale.copy().range([margin.left, width - margin.right])
Insert cell
// Remember (0, 0) is the top-left hand corner
yMargin = yscale.copy().range([height - margin.bottom, margin.top])
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height)
.style('border', '1px dotted #999'); // <-- A dotted border helps us see the added margin

const g = container.selectAll('g')
.data(barData)
.enter()
.append('g')
.attr('transform', d => `translate(${margin.left}, ${yMargin(d.key)})`);

g.append('rect')
.attr('width', d => xMargin(d.value) - xMargin(0)) // <-- We must subtract our left offset.
.attr('height', yMargin.bandwidth())
.style('fill', d => color(d.key))
.style('stroke', 'white');

g.append('text')
.attr('x', d => xMargin(d.value) - xMargin(0)) // <-- We must subtract our left offset.
.attr('dx', -20)
.attr('dy', '1em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

return container.node();
}
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height)
.style('border', '1px dotted #999');

container.append('g')
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(xMargin));

container.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yMargin));

return container.node();
}
Insert cell
Insert cell
{
const container = d3.create('svg')
.attr('width', width)
.attr('height', height);

const g = container.selectAll('g')
.data(barData)
.enter()
.append('g')
.attr('transform', d => `translate(${margin.left}, ${yMargin(d.key)})`);

g.append('rect')
.attr('width', d => xMargin(d.value) - xMargin(0))
.attr('height', yMargin.bandwidth())
.style('fill', d => color(d.key))
.style('stroke', 'white');

g.append('text')
.attr('x', d => xMargin(d.value) - xMargin(0))
.attr('dx', -20)
.attr('dy', '1em')
.attr('fill', 'white')
.style('font-size', 'small')
.text(d => d.value);

container.append('g')
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(xMargin));

container.append('g')
.attr('transform', `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yMargin));

return container.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more